package ai.people.platform.information.service.impl;

import ai.people.core.common.util.ApplicationContextUtil;
import ai.people.core.information.api.InformationStanderSearchService;
import ai.people.netmon.framework.domain.archives.dto.*;
import ai.people.netmon.framework.exception.enums.InformationSearchExceptionEnum;
import ai.people.netmon.framework.exception.type.BusinessException;
import ai.people.platform.information.context.InformationServiceContext;
import ai.people.platform.information.converter.StandardConverter;
import ai.people.platform.information.properties.DataSourceProperties;
import ai.people.platform.information.util.SHA256Util;
import cn.hutool.core.map.MapUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;

/**
 * 信息搜索服务
 *
 * @author LiGuanNan
 * @date 2022/6/6 20:16
 */
@Slf4j
public class InformationStanderSearchServiceImpl implements InformationStanderSearchService {

    private DataSourceProperties dataSourceProperties;

    public InformationStanderSearchServiceImpl(DataSourceProperties dataSourceProperties) {
        this.dataSourceProperties = dataSourceProperties;
    }

    /**
     * 获取信息搜索源唯一标识
     *
     * @return {@link String}
     */
    @Override
    public String getInformationSearchSource() {
        return dataSourceProperties.getName();
    }

    /**
     * 数据源是否可用
     *
     * @return {@link Boolean}
     */
    @Override
    public Boolean isEnable() {
        return dataSourceProperties.isEnable();
    }


    /**
     * 信息检索
     * @param reqParam
     * @return
     */
    @Override
    public ThirdPartyCommonRespDTO<ThirdPartySearchPageResponse<ThirdPartyInformationSearchRespDTO>> search(InformationThirdPartySearchReqDTO reqParam) {
        if(!isEnable()){
            return ThirdPartyCommonRespDTO.<ThirdPartySearchPageResponse<ThirdPartyInformationSearchRespDTO>>builder()
                    .code(InformationSearchExceptionEnum.SERVER_UNAVAILABLE.getCode())
                    .msg(InformationSearchExceptionEnum.SERVER_UNAVAILABLE.getMessage())
                    .data(ThirdPartySearchPageResponse.builderEmpty())
                    .build();
        }

        DataSourceProperties.InformationServer search = dataSourceProperties.getSearch();
        if(search == null || StringUtils.isBlank(search.getUrl())){
            throw new BusinessException(InformationSearchExceptionEnum.SEARCH_CONFIGURATION_ERROR);
        }

        //组装请求参数
        String bodyParam = getSearchRequestPram(reqParam);
        log.info("【{}】信息查询数据检索请求参数：{}", getInformationSearchSource(), bodyParam);

        // 添加请求头认证信息
        Map<String, String> heads = getAuthHeader();

        Stopwatch elapsedTime = Stopwatch.createStarted();
        HttpResponse response = HttpRequest.post(search.getUrl())
                .headerMap(heads, false)
                .body(bodyParam)
                .timeout(60 * 1000)
                .execute();
        elapsedTime.stop();

        log.info("【{}】信息查询数据检索接口响应数据：【{}】", getInformationSearchSource(), response);
        log.warn("【{}】信息查询数据检索接口接口耗时: 【{}】", getInformationSearchSource(), elapsedTime);

        if(response != null && StringUtils.isNotBlank(response.body())){
            JSONObject resultJson = JSONObject.parseObject(response.body());
            Integer code = resultJson.getInteger("code");
            if(ThirdPartyCommonRespDTO.isSuccess(code) && StringUtils.isNotBlank(resultJson.getString("data"))){
                ThirdPartySearchPageResponse<ThirdPartyInformationSearchRespDTO> result = getSearchResult(resultJson.getJSONObject("data"));
                return ThirdPartyCommonRespDTO.<ThirdPartySearchPageResponse<ThirdPartyInformationSearchRespDTO>>builder()
                        .code(0)
                        .msg(null)
                        .data(result)
                        .build();
            }
            return ThirdPartyCommonRespDTO.<ThirdPartySearchPageResponse<ThirdPartyInformationSearchRespDTO>>builder()
                    .code(code)
                    .msg(resultJson.getString("msg"))
                    .data(ThirdPartySearchPageResponse.builderEmpty())
                    .build();
        }
        return ThirdPartyCommonRespDTO.<ThirdPartySearchPageResponse<ThirdPartyInformationSearchRespDTO>>builder()
                .code(-1)
                .msg("请求失败，响应数据为空")
                .data(ThirdPartySearchPageResponse.builderEmpty())
                .build();
    }

    /**
     * 信息检索v2版本，会将检索结果记录到redis中
     *
     * @param reqParam
     * @return
     */
    @Override
    public ThirdPartyCommonRespDTO<ThirdPartySearchPageResponse<ThirdPartyInformationSearchRespDTO>> searchV2(InformationThirdPartySearchReqDTO reqParam) {
        ThirdPartyCommonRespDTO<ThirdPartySearchPageResponse<ThirdPartyInformationSearchRespDTO>> respDto = search(reqParam);
        //将检索结果记录到redis中
        RedisTemplate<Object, Object> redisTemplate = ApplicationContextUtil.getBean(RedisTemplate.class, "redisTemplate");
        InformationServiceContext context = ApplicationContextUtil.getBean(InformationServiceContext.class, "informationServiceContext");
        HashOperations<Object, String, Object> opsForHash = redisTemplate.opsForHash();
        opsForHash.put(context.getSearchProgressKey(getSid()), getInformationSearchSource(), 1);
        //记录响应数据
        opsForHash.put(context.getSearchResultKey(getSid()), getInformationSearchSource(), respDto);
        return respDto;
    }

    /**
     * 获取认证相关的header信息
     *
     * @return {@link Map}<{@link String}, {@link String}>
     */
    private Map<String, String> getAuthHeader() {
        Map<String, String > heads = new HashMap<>();
        heads.put("Content-Type", "application/json;charset=UTF-8");
        String appId = dataSourceProperties.getAppId();
        String appSecret = dataSourceProperties.getAppSecret();
        if(StringUtils.isBlank(appId) || StringUtils.isBlank(appSecret)){
            log.error("数据源认证参数配置错误，appId:{}, appSecret:{}", appId, appSecret);
        }else {
            long timestamp = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().getEpochSecond();
            long nonce = new Random().nextInt(10000);
            StringBuilder stringBuilder = new StringBuilder();
            StringBuilder append = stringBuilder.append(appId).append(timestamp).append(nonce).append(appSecret);
            String sign = SHA256Util.getSHA256(append.toString());
            heads.put("appId", appId);
            heads.put("timestamp", timestamp + "");
            heads.put("nonce", nonce + "");
            heads.put("sign", sign);
        }
        return heads;
    }

    /**
     * 得到搜索结果
     *
     * @param data 数据
     * @return {@link ThirdPartySearchPageResponse}
     */
    private ThirdPartySearchPageResponse<ThirdPartyInformationSearchRespDTO> getSearchResult(JSONObject data) {
        ThirdPartySearchPageResponse<ThirdPartyInformationSearchRespDTO> thirdPartySearchPageResponse = ThirdPartySearchPageResponse.builderEmpty();
        List<ThirdPartyInformationSearchRespDTO> thirdPartyInformationSearchRespList = Lists.newArrayList();

        DataSourceProperties.InformationServer search = dataSourceProperties.getSearch();
        Long total = data.getLong("total");
        String converterPath = search.getConverter();
        if(StringUtils.isNotBlank(converterPath)){
            //自定义参数转换逻辑
            StandardConverter converter = getStandardConverter(converterPath);
            if(converter != null){
                thirdPartyInformationSearchRespList = converter.searchResponseParamConvert(data);
            }
        }else if(!CollectionUtils.isEmpty(search.getRespMapping())){
            //mapping映射参数转换
            Map<String, String> respMapping = search.getRespMapping();
            thirdPartySearchPageResponse = data.toJavaObject(ThirdPartySearchPageResponse.class);
            if(data.containsKey("list")){
                JSONArray list = data.getJSONArray("list");
                list.forEach(o->{
                    JSONObject rowJson = (JSONObject) o;
                    JSONObject fields = rowJson.getJSONObject("fields");
                    if(!MapUtil.isEmpty(fields)){
                        respMapping.keySet().forEach(key->{
                            fields.put(respMapping.get(key), fields.remove(key));
                        });
                    }
                });
                thirdPartyInformationSearchRespList = list.toJavaList(ThirdPartyInformationSearchRespDTO.class);
            }
        }else {
            //默认处理逻辑
            thirdPartySearchPageResponse = data.toJavaObject(ThirdPartySearchPageResponse.class);
            if(data.containsKey("list")){
                thirdPartyInformationSearchRespList = data.getJSONArray("list").toJavaList(ThirdPartyInformationSearchRespDTO.class);
            }
        }

        //字段排序
        if(!CollectionUtils.isEmpty(thirdPartyInformationSearchRespList) && !CollectionUtils.isEmpty(search.getFieldShowOrder())){
            thirdPartyInformationSearchRespList.forEach(o->{
                o.setFields(sortFieldShowOrder(o.getFields(), search.getFieldShowOrder()));
            });
        }

        thirdPartySearchPageResponse.setList(thirdPartyInformationSearchRespList);
        thirdPartySearchPageResponse.setTotal(total);
        return thirdPartySearchPageResponse;
    }

    /**
     * 按照字段显示顺序排序
     *
     * @param fields         字段map
     * @param fieldShowOrder 字段显示顺序
     * @return {@link Map}<{@link String}, {@link String}>
     */
    private Map<String, Object> sortFieldShowOrder(Map<String, Object> fields, List<String> fieldShowOrder) {
        if (fields == null || fields.isEmpty()) {
            return null;
        }
        Map<String, Object> sortMap = new TreeMap<>((o1, o2) -> {
            if (fieldShowOrder.contains(o1) && fieldShowOrder.contains(o2)) {
                return fieldShowOrder.indexOf(o1) - fieldShowOrder.indexOf(o2);
            }else if (fieldShowOrder.contains(o2)){
                return 1;
            }
            return -1;
        });
        sortMap.putAll(fields);
        return sortMap;
    }

    /**
     * 得到标准转换器
     *
     * @param converterBeanId 转换器路径
     * @return {@link StandardConverter}
     */
    private StandardConverter getStandardConverter(String converterBeanId) {
        StandardConverter converter = null;
        try {
            converter = (StandardConverter) ApplicationContextUtil.getBean(converterBeanId);
        } catch (Exception e) {
            log.error("转换器id错误， converter：【{}】", converter);
            e.printStackTrace();
        }
        return converter;
    }

    /**
     * 得到搜索请求参数
     *
     * @param reqParam 要求参数
     * @return {@link String}
     */
    private String getSearchRequestPram(InformationThirdPartySearchReqDTO reqParam) {
        DataSourceProperties.InformationServer search = dataSourceProperties.getSearch();
        JSONObject param = new JSONObject();

        //通过converter处理参数映射
        String converterPath = search.getConverter();
        if(StringUtils.isNotBlank(converterPath)){
            StandardConverter converter = getStandardConverter(converterPath);
            if(converter != null){
                return converter.searchRequestParamConvert(reqParam);
            }
        }
        //通过req-mapping映射请求参数
        Map<String, String> reqMapping = search.getReqMapping();
        if(!CollectionUtils.isEmpty(reqMapping)){
            JSONObject reqData = JSONObject.parseObject(JSONObject.toJSONString(reqParam));
            reqMapping.keySet().forEach(k->{
                if(reqData.containsKey(k)){
                    reqData.put(reqMapping.get(k),reqData.remove(k));
                }
            });
            param.put("data", reqData);
        }else {
            //默认处理
            param.put("data", reqParam);
        }
        return param.toJSONString();
    }

    /**
     * 详情
     *
     * @param dataId 数据标识
     * @return {@link ThirdPartyCommonRespDTO}<{@link ThirdPartyInformationDetailRespDTO}>
     */
    @Override
    public ThirdPartyCommonRespDTO<ThirdPartyInformationDetailRespDTO> detail(String dataId) {
        if(!isEnable()){
            return ThirdPartyCommonRespDTO.<ThirdPartyInformationDetailRespDTO>builder()
                    .code(InformationSearchExceptionEnum.SERVER_UNAVAILABLE.getCode())
                    .msg(InformationSearchExceptionEnum.SERVER_UNAVAILABLE.getMessage())
                    .data(null)
                    .build();
        }

        DataSourceProperties.InformationServer search = dataSourceProperties.getDetail();

        if(search == null){
            throw new BusinessException(InformationSearchExceptionEnum.DETAIL_CONFIGURATION_ERROR);
        }

        //创建json对象作为requestBody
        String bodyParam = getDetailRequestParam(dataId);
        log.info("信息查询数据详情请求参数：{}", bodyParam);

        // 添加请求头信息
        Map<String, String> heads = getAuthHeader();

        HttpResponse response = HttpRequest.post(dataSourceProperties.getDetail().getUrl())
                .headerMap(heads, false)
                .body(bodyParam)
                .timeout(60 * 1000)
                .execute();

        log.info("信息查询数据详情接口响应数据：【{}】", response);

        if(response != null && StringUtils.isNotBlank(response.body())){
            JSONObject resultJson = JSONObject.parseObject(response.body());
            Integer code = resultJson.getInteger("code");
            if(ThirdPartyCommonRespDTO.isSuccess(code) && resultJson.getJSONObject("data") != null){
                Map<String, Object> map = getDetailRespResultMap(resultJson.getJSONObject("data"));
                return ThirdPartyCommonRespDTO.<ThirdPartyInformationDetailRespDTO>builder()
                        .code(code)
                        .msg(resultJson.getString("msg"))
                        .data(ThirdPartyInformationDetailRespDTO.builder().fields(map).build())
                        .build();
            }
        }
        return ThirdPartyCommonRespDTO.<ThirdPartyInformationDetailRespDTO>builder()
                .code(-1)
                .msg("请求失败，响应数据为空")
                .data(null)
                .build();
    }

    /**
     * 得到详情接口响应结果
     *
     * @param data 数据
     * @return {@link Map}<{@link String}, {@link String}>
     */
    private Map<String, Object> getDetailRespResultMap(JSONObject data) {
        Map<String, Object> fieldMap = null;
        JSONObject fields = data.getJSONObject("fields");
        DataSourceProperties.InformationServer detail = dataSourceProperties.getDetail();
        String converterPath = detail.getConverter();
        if(StringUtils.isNotBlank(converterPath)){
            //自定义转换逻辑
            StandardConverter converter = getStandardConverter(converterPath);
            if(converter != null){
                return converter.detailResponseParamConvert(fields);
            }
        }else if(!CollectionUtils.isEmpty(detail.getRespMapping())) {
            //mapping映射转换逻辑
            fieldMap = fields;
            Map<String, String> respMapping = detail.getRespMapping();
            respMapping.keySet().forEach(key->{
                if(fields.containsKey(key)){
                    fields.put(respMapping.get(key), fields.remove(key));
                }
            });
        }else {
            //默认处理
            fieldMap = fields;
        }

        //字段排序
        if(!CollectionUtils.isEmpty(fieldMap) && !CollectionUtils.isEmpty(detail.getFieldShowOrder())){
            fieldMap = sortFieldShowOrder(fieldMap, detail.getFieldShowOrder());
        }
        return fieldMap;
    }

    /**
     * 得到详细请求参数
     *
     * @param dataId 数据标识
     * @return {@link String}
     */
    private String getDetailRequestParam(String dataId) {
        DataSourceProperties.InformationServer detail = dataSourceProperties.getDetail();
        String converterPath = detail.getConverter();
        if(StringUtils.isNotBlank(converterPath)){
            //自定义参数转换器
            StandardConverter converter = getStandardConverter(converterPath);
            if(converter != null){
                return converter.detailRequestParamConvert(dataId);
            }
        }
        JSONObject data = new JSONObject();
        Map<String, String> reqMapping = detail.getReqMapping();
        if(!CollectionUtils.isEmpty(reqMapping)){
            //通过req-mapping映射请求参数
            data.put(reqMapping.get("dataId"), dataId);
        }else {
            //默认处理
            data.put("dataId", dataId);
        }

        JSONObject param = new JSONObject();
        param.put("version", "");
        param.put("data", data);
        return param.toJSONString();
    }
}
